﻿using River.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace River.IntAI.ISdk
{
    /// <summary>
    /// 音频识别文字
    /// </summary>
    public class SpeechUtils
    {
        public static bool ConvertFlag = false;
        public delegate void SpeedCallEndEvent(RetSpeech speechObj);// 语音识别事件
        public delegate void SynthesisEvent(string msg);// 语音合成事件
        /// <summary>
        /// 语音转文字
        /// </summary>
        /// <param name="callEndEvent"></param>
        /// <param name="path"></param>
        /// <param name="samplerate"></param>
        /// <param name="chanels"></param>
        /// <param name="bits_per_sample"></param>
        public static void ConvertSpeedToWord(SpeedCallEndEvent callEndEvent, string path, int samplerate, int chanels = 1, int bits_per_sample = 16)
        {
            try
            {
                ConvertFlag = true;
                string appId, apiKey, secretKey;
                if (!RiverAuth.Instant.GetSpeechApiConfig(false, out appId, out apiKey, out secretKey))// 读取应用配置
                {
                    return;
                }
                var client = new Baidu.Aip.Speech.Asr(appId, apiKey, secretKey);
                client.Timeout = (int)ConfigUtil.GetConfig(EnumConfigKey.ClientTimeOut, 60 * 1000);
                var data = File.ReadAllBytes(path);
                System.Collections.Generic.Dictionary<string, string> setting = IniUtil.INIGetAllItems(IniSection.SpeechOptions);
                System.Collections.Generic.Dictionary<string, object> options = new Dictionary<string, object>();
                setting.ToList().ForEach(x => { options.Add(x.Key, x.Value); });
                string format = Path.GetExtension(path).Replace(".", "");
                // pcm 文件超长时自动分段识别，每段60各种
                if (format.ToLower() == "pcm")// pcm 可以分段识别
                {
                    long perVol = calVol(samplerate, chanels, bits_per_sample);
                    StringBuilder sb = new StringBuilder();
                    long cur_index = 0;
                    while (ConvertFlag && cur_index < data.Length)
                    {
                        long cur_size = cur_index + perVol > data.Length ? data.Length - cur_index : perVol;
                        byte[] temp = new byte[cur_size];
                        Array.Copy(data, cur_index, temp, 0, cur_size);
                        // 识别
                        var result = client.Recognize(temp, format, samplerate, options);
                        RetSpeech retSpeech = result.ToObject<RetSpeech>();
                        callEndEvent?.Invoke(retSpeech);
                        if (retSpeech.err_no != 0)
                            break;
                        cur_index += cur_size;
                    }
                }
                else// 其余格式只能全部上传识别（时长不能超过60秒）
                {
                    var result = client.Recognize(data, format, samplerate, options);
                    RetSpeech retSpeech = result.ToObject<RetSpeech>();
                    callEndEvent?.Invoke(retSpeech);
                }
            }
            catch (Exception ex)
            {
                MsgUtil.Error("识别错误:" + ex.Message);
            }
            finally
            {
            }
        }

        // 数据量（字节/秒） = 采样率（Hz） * 采样大小（bit） * 声道数  / 8 * 时长
        public static long calVol(int samplerate, int chanels, int bits_per_sample, int time = 60)
        {
            return samplerate * bits_per_sample * chanels / 8 * time;
        }
        /// <summary>
        /// 语音合成
        /// </summary>
        /// <param name="reqItem">请求相关参数</param>
        /// <param name="destPath">mp3文件保存路径</param>
        /// <param name="synthesisEvent">状态事件</param>
        /// <returns></returns>
        public static bool Synthesis(ReqSynthesis reqItem, string destPath, SynthesisEvent synthesisEvent)
        {
            bool callResult = false;

            try
            {
                synthesisEvent("语音合成初始化...");
                string appId, apiKey, secretKey;
                if (!RiverAuth.Instant.GetSpeechApiConfig(true, out appId, out apiKey, out secretKey))// 读取应用配置
                {
                    synthesisEvent("当天使用次数已达上限或配置无效。");
                    return false;
                }
                var client = new Baidu.Aip.Speech.Tts(apiKey, secretKey);
                client.Timeout = 60000;  // 修改超时时间
                var option = new Dictionary<string, object>()
                {
                    {"vol", reqItem.vol}, // 
                    { "pit", reqItem.pit}, // 
                    {"spd", reqItem.spd}, // 语速
                    {"per", reqItem.per}  // 发音人，4：情感度丫丫童声
                };
                synthesisEvent("开始语音合成...");
                var result = client.Synthesis(reqItem.tex, option);
                // 保存文件
                if (result.Success)  // 或 result.Success
                {
                    File.WriteAllBytes(destPath, result.Data);
                    synthesisEvent("语音合成返回成功，文件：" + destPath);
                    callResult = true;
                }
                else
                {
                    synthesisEvent(string.Format("语音合成失败, 错误码：[{0}], 原因：{1}", result.ErrorCode, result.ErrorMsg));
                }
            }
            catch (Exception ex)
            {
                synthesisEvent("语音合成失败，原因：" + ex.Message);
                LogHelper.LogError(ex, "语音合成");
            }
            return callResult;
        }
    }
    /// <summary>
    /// 语音合成请求
    /// </summary>
    [Serializable]
    public class ReqSynthesis
    {
        public string tex { get; set; }//合成的文本，使用UTF-8编码，请注意文本长度必须小于1024字节 必填项
        public string cuid { get; set; }// 用户唯一标识，用来区分用户，填写机器 MAC 地址或 IMEI 码，长度为60以内 否
        public int spd { get; set; }//语速，取值0-9，默认为5中语速 否
        public int pit { get; set; }//音调，取值0-9，默认为5中语调 否
        public int vol { get; set; }//音量，取值0-15，默认为5中音量 否
        public int per { get; set; }//发音人选择, 0为女声，1为男声，3为情感合成-度逍遥，4为情感合成-度丫丫，默认为普通女
    }
    /// <summary>
    /// 语音转文字返回结果
    /// </summary>
    public class RetSpeech
    {
        public string corpus_no { get; set; }
        public string err_msg { get; set; }
        public int err_no { get; set; }
        public string[] result { get; set; }
        public string sn { get; set; }
    }
}
